﻿using Microsoft.Extensions.Configuration;
using SD.App.Desktop.Entity;
using SD.App.Desktop.Model;
using SD.App.Desktop.Repository;
using SD.App.Desktop.Utils;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace SD.App.Desktop.Service
{
    /// <summary>
    /// 用户业务服务实现类
    /// </summary>
    public class UserService : IUserService, ITransientLifetime
    {
        private readonly ILogger _logger;
        private readonly EncryptionHelper _encryptionHelper; // 注入非静态工具类
        private readonly IUserRepository _userRepository;
        private readonly IResetPasswordTokenRepository _resetTokenRepository; // 新增令牌 Repository

        /// <summary>
        /// 构造函数注入依赖（Repository + Logger）
        /// </summary>
        public UserService(ILogger logger, 
            EncryptionHelper encryptionHelper,
            IUserRepository userRepository,
            IResetPasswordTokenRepository resetTokenRepository)
        {


            _logger = logger;
            _encryptionHelper = encryptionHelper;

            _userRepository = userRepository;
            _resetTokenRepository = resetTokenRepository;
        }

        #region 登录验证核心逻辑
        public async Task<User?> LoginAsync(string username, string plainPassword)
        {
            try
            {
                _logger.Information("用户登录验证：用户名={Username}", username);

                // 1. 参数校验（避免空值）
                if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(plainPassword))
                {
                    _logger.Warning("登录失败：用户名或密码为空");
                    return null;
                }

                // 2. 从数据库查询用户（通过 Repository 访问数据）
                var user = await _userRepository.GetUserByUsernameAsync(username);
                if (user == null)
                {
                    _logger.Warning("登录失败：用户名 {Username} 不存在", username);
                    return null;
                }

                // 3. 校验用户状态（是否已激活）
                if (!user.IsActive)
                {
                    _logger.Warning("登录失败：用户 {Username} 未激活", username);
                    return null;
                }

                // 4. 校验密码（明文密码加密后与数据库密码对比）
                bool passwordValid = VerifyPassword(plainPassword, user.Password);
                if (!passwordValid)
                {
                    _logger.Warning("登录失败：用户 {Username} 密码错误", username);
                    return null;
                }

                // 5. 登录成功（可扩展：记录登录日志、更新最后登录时间等）
                _logger.Information("用户 {Username} 登录成功，用户ID={UserId}", username, user.Id);
                return user;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "用户登录验证异常：用户名={Username}", username);
                throw; // 抛出异常，让上层处理（如显示错误提示）
            }
        }
        #endregion

        #region 密码加密与验证（SHA256 + 盐值）
        public string EncryptPassword(string plainPassword)
        {
            return _encryptionHelper.EncryptPassword(plainPassword);
        }

        public bool VerifyPassword(string plainPassword, string encryptedPassword)
        {
            return _encryptionHelper.VerifyPassword(plainPassword, encryptedPassword);
        }
        #endregion

        #region 用户状态相关业务
        public async Task<bool> CheckUserActiveAsync(long userId)
        {
            try
            {
                _logger.Debug("检查用户激活状态：用户ID={UserId}", userId);
                var user = await _userRepository.GetByIdAsync(userId);
                return user != null && user.IsActive;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "检查用户激活状态异常：用户ID={UserId}", userId);
                return false;
            }
        }

        public async Task<bool> ActivateUserAsync(long userId)
        {
            try
            {
                _logger.Debug("激活用户账号：用户ID={UserId}", userId);
                // 调用 Repository 的按条件更新方法
                int affected = await _userRepository.UpdateByConditionAsync(
                    whereExpression: u => u.Id == userId,
                    updateExpression: u => new User { IsActive = true }
                );
                bool success = affected > 0;
                _logger.Information("激活用户 {UserId} 结果：{Success}", userId, success);
                return success;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "激活用户账号异常：用户ID={UserId}", userId);
                return false;
            }
        }
        #endregion

        #region 密码重置实现
        public async Task<string> ApplyPasswordResetAsync(string username, string email)
        {
            try
            {
                _logger.Information("用户申请密码重置：用户名={Username}，邮箱={Email}", username, email);

                // 1. 校验用户是否存在（可扩展：校验邮箱与注册邮箱一致）
                var user = await _userRepository.GetUserByUsernameAsync(username);
                if (user == null)
                {
                    _logger.Warning("密码重置失败：用户名 {Username} 不存在", username);
                    throw new InvalidOperationException("用户名不存在");
                }

                // 2. 生成随机临时令牌（32 位 GUID）
                string token = Guid.NewGuid().ToString("N");
                // 3. 设置令牌过期时间（15 分钟）
                DateTime expireTime = DateTime.Now.AddMinutes(15);

                // 4. 存储令牌到数据库
                await _resetTokenRepository.AddAsync(new ResetPasswordToken
                {
                    Username = username,
                    Token = token,
                    ExpireTime = expireTime,
                    IsUsed = false
                });

                // 5. 发送重置链接到用户邮箱（实际项目中调用邮件服务）
                _logger.Information("密码重置链接已发送：用户名={Username}，令牌={Token}，过期时间={ExpireTime}",
                    username, token, expireTime);
                // 示例：发送邮件逻辑（需集成邮件服务）
                // await _emailService.SendResetPasswordEmailAsync(email, username, token);

                return token;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "密码重置申请失败：用户名={Username}", username);
                throw;
            }
        }

        public async Task<bool> VerifyResetTokenAsync(string username, string token)
        {
            try
            {
                _logger.Debug("验证密码重置令牌：用户名={Username}，令牌={Token}", username, token);
                var resetToken = await _resetTokenRepository.GetByUsernameAndTokenAsync(username, token);
                bool valid = resetToken != null;
                _logger.Debug("令牌验证结果：{Valid}", valid);
                return valid;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "令牌验证失败：用户名={Username}，令牌={Token}", username, token);
                return false;
            }
        }

        public async Task<bool> ResetPasswordAsync(string username, string token, string newPlainPassword)
        {
            try
            {
                _logger.Information("执行密码重置：用户名={Username}，令牌={Token}", username, token);

                // 1. 验证令牌有效性
                bool tokenValid = await VerifyResetTokenAsync(username, token);
                if (!tokenValid)
                {
                    _logger.Warning("密码重置失败：令牌无效或已过期");
                    throw new InvalidOperationException("令牌无效或已过期");
                }

                // 2. 校验新密码复杂度（可扩展：长度、包含大小写等）
                if (string.IsNullOrWhiteSpace(newPlainPassword) || newPlainPassword.Length < 6)
                {
                    _logger.Warning("密码重置失败：新密码不符合要求");
                    throw new InvalidOperationException("新密码长度不能少于 6 位");
                }

                // 3. 加密新密码
                string newEncryptedPassword = EncryptPassword(newPlainPassword);

                // 4. 更新用户密码
                var user = await _userRepository.GetUserByUsernameAsync(username);

                if (user == null)
                {
                    _logger.Warning($"用户{username}不存在");
                    throw new EntryPointNotFoundException($"用户{username}不存在");
                }

                user.Password = newEncryptedPassword;
                int affected = await _userRepository.UpdateAsync(user);

                if (affected > 0)
                {
                    // 5. 标记令牌为已使用（防止重复重置）
                    var resetToken = await _resetTokenRepository.GetByUsernameAndTokenAsync(username, token);

                    if (resetToken == null)
                    {
                        _logger.Warning("密码重置失败：令牌无效或已过期");
                        throw new InvalidOperationException("令牌无效或已过期");
                    }

                    resetToken.IsUsed = true;
                    await _resetTokenRepository.UpdateAsync(resetToken);

                    _logger.Information("密码重置成功：用户名={Username}", username);
                    return true;
                }

                _logger.Warning("密码重置失败：更新用户密码无效果");
                return false;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "密码重置执行失败：用户名={Username}", username);
                throw;
            }
        }
        #endregion

        #region 新增注册核心逻辑
        public async Task<User?> RegisterAsync(RegisterDto registerDto)
        {
            try
            {
                _logger.Information("用户注册：用户名={Username}", registerDto.Username);

                // 1. 基础参数校验（DTO 注解校验+自定义校验）
                if (!ValidateRegisterParams(registerDto, out string errorMsg))
                {
                    _logger.Warning("注册失败：参数校验不通过，原因={ErrorMsg}", errorMsg);
                    throw new InvalidOperationException(errorMsg);
                }

                // 2. 用户名唯一性校验
                bool usernameExists = await IsUsernameExistsAsync(registerDto.Username);
                if (usernameExists)
                {
                    _logger.Warning("注册失败：用户名 {Username} 已存在", registerDto.Username);
                    throw new InvalidOperationException("用户名已被占用");
                }

                // 3. 密码加密（复用原有 SHA256+盐值逻辑）
                string encryptedPassword = EncryptPassword(registerDto.Password);

                // 4. 敏感信息加密（手机号用 AES 加密存储，可选）
                string? encryptedPhone = string.IsNullOrEmpty(registerDto.Phone)
                    ? null
                    : _encryptionHelper.AesEncrypt(registerDto.Phone);

                // 5. 构建用户实体（默认配置：已激活、创建时间自动赋值）
                var newUser = new User
                {
                    Username = registerDto.Username,
                    Password = encryptedPassword,
                    Email = registerDto.Email ?? string.Empty,
                   // Phone = encryptedPhone, // 存储加密后的手机号
                    IsActive = true, // 注册后默认激活（可改为 false，需手动激活）
                    CreateTime = DateTime.Now
                };

                // 6. 数据入库（调用通用 Repository 新增方法）
                newUser = await _userRepository.AddAsync(newUser);
                if (newUser.Id < 0)
                {
                    _logger.Error("注册失败：用户入库失败，用户名={Username}", registerDto.Username);
                    throw new InvalidOperationException("注册失败，请重试");
                }

                // 7. 注册成功（返回完整用户实体）
                _logger.Information("用户注册成功：用户名={Username}，用户ID={UserId}", registerDto.Username, newUser.Id);
                return newUser;
            }
            catch (InvalidOperationException ex)
            {
                // 业务异常（参数错误、用户名已存在等），直接抛出
                throw;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "用户注册异常：用户名={Username}", registerDto.Username);
                throw new InvalidOperationException("注册异常，请联系管理员");
            }
        }

        /// <summary>
        /// 注册参数校验（DTO 注解+自定义逻辑）
        /// </summary>
        /// <returns>true：校验通过；false：校验失败，errorMsg 为失败原因</returns>
        private bool ValidateRegisterParams(RegisterDto dto, out string errorMsg)
        {
            errorMsg = string.Empty;

            // 1. DTO 注解校验（使用 DataAnnotations 验证）
            var validationContext = new System.ComponentModel.DataAnnotations.ValidationContext(dto);
            var validationResults = new System.Collections.Generic.List<System.ComponentModel.DataAnnotations.ValidationResult>();
            bool isValid = System.ComponentModel.DataAnnotations.Validator.TryValidateObject(dto, validationContext, validationResults, true);

            if (!isValid)
            {
                errorMsg = validationResults[0].ErrorMessage ?? "参数格式不正确";
                return false;
            }

            // 2. 自定义校验：密码与确认密码一致
            if (!string.Equals(dto.Password, dto.ConfirmPassword, StringComparison.Ordinal))
            {
                errorMsg = "密码与确认密码不一致";
                return false;
            }

            // 3. 自定义校验：密码复杂度
            if (!ValidatePasswordComplexity(dto.Password))
            {
                errorMsg = "密码需包含大小写字母、数字、特殊字符中至少两种";
                return false;
            }

            return true;
        }

        /// <summary>
        /// 校验用户名是否已存在（实现接口方法）
        /// </summary>
        public async Task<bool> IsUsernameExistsAsync(string username)
        {
            if (string.IsNullOrWhiteSpace(username))
                return false;

            try
            {
                // 调用 UserRepository 特有方法
                var user = await _userRepository.GetUserByUsernameAsync(username);
                return user != null;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "校验用户名是否存在异常：用户名={Username}", username);
                return false;
            }
        }

        /// <summary>
        /// 校验密码复杂度（实现接口方法）
        /// </summary>
        public bool ValidatePasswordComplexity(string plainPassword)
        {
            if (string.IsNullOrWhiteSpace(plainPassword))
                return false;

            // 密码复杂度规则：含大小写字母、数字、特殊字符（!@#$%^&*()_+-=）中至少两种
            bool hasUpper = false; // 大写字母
            bool hasLower = false; // 小写字母
            bool hasDigit = false; // 数字
            bool hasSpecial = false; // 特殊字符
            string specialChars = "!@#$%^&*()_+-=";

            foreach (char c in plainPassword)
            {
                if (char.IsUpper(c)) hasUpper = true;
                else if (char.IsLower(c)) hasLower = true;
                else if (char.IsDigit(c)) hasDigit = true;
                else if (specialChars.Contains(c)) hasSpecial = true;
            }

            // 统计满足的条件数量，至少两种
            int validCount = 0;
            if (hasUpper) validCount++;
            if (hasLower) validCount++;
            if (hasDigit) validCount++;
            if (hasSpecial) validCount++;

            return validCount >= 2;
        }
        #endregion
    }
}
